package com.gjy.map;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 宫静雨
 * @version 1.0
 * @since 2023-04-24 09:50:47
 */
public class GeoLocationMerger {

    private static final int MAX_DISTANCE = 1; // 合并点的最大距离半径（单位：千米）

    public static void main(String[] args) {
        // 定义原始点的列表
        List<GeoLocation> originalLocations = new ArrayList<>();
        originalLocations.add(new GeoLocation(39.9042, 116.4074));
        originalLocations.add(new GeoLocation(39.9142, 116.4274));
        originalLocations.add(new GeoLocation(39.9342, 116.4474));
        originalLocations.add(new GeoLocation(39.9044, 116.4074));
        originalLocations.add(new GeoLocation(39.9144, 116.4274));
        originalLocations.add(new GeoLocation(39.9344, 116.4474));

        // 定义合并后的点的列表
        List<GeoLocation> mergedLocations = new ArrayList<>();

        // 遍历原始点的列表
        for (GeoLocation location : originalLocations) {
            boolean merged = false;
            // 遍历合并后点的列表
            for (GeoLocation mergedLocation : mergedLocations) {
                // 如果原始点与合并后的点距离在允许的最大半径内，合并到同一个点
                if (location.distanceTo(mergedLocation) <= MAX_DISTANCE) {
                    mergedLocation.addLocation(location);
                    merged = true;
                    break;
                }
            }
            // 如果原始点没有与任何一个合并后的点合并，将其加入合并后的点的列表
            if (!merged) {
                mergedLocations.add(new GeoLocation(location));
            }
        }

        // 打印合并后的点的列表
        for (GeoLocation location : mergedLocations) {
            System.out.println(location.toString());
        }
    }
}

class GeoLocation {
    private final double latitude;
    private final double longitude;
    private final List<GeoLocation> locations = new ArrayList<>();

    public GeoLocation(double latitude, double longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.locations.add(this);
    }

    public GeoLocation(GeoLocation location) {
        this.latitude = location.getLatitude();
        this.longitude = location.getLongitude();
        this.locations.add(location);
    }

    public void addLocation(GeoLocation location) {
        this.locations.add(location);
    }

    public double getLatitude() {
        return this.latitude;
    }

    public double getLongitude() {
        return this.longitude;
    }

    // 计算两点之间的距离
    public double distanceTo(GeoLocation location) {
        double radLatitude1 = Math.toRadians(getLatitude());
        double radLatitude2 = Math.toRadians(location.getLatitude());
        double radLongitude1 = Math.toRadians(getLongitude());
        double radLongitude2 = Math.toRadians(location.getLongitude());
        double a = Math.sin((radLatitude1 - radLatitude2) / 2) * Math.sin((radLatitude1 - radLatitude2) / 2)
                + Math.cos(radLatitude1) * Math.cos(radLatitude2) * Math.sin((radLongitude1 - radLongitude2) / 2) * Math.sin((radLongitude1 - radLongitude2) / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return 6371 * c;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("(").append(getLatitude()).append(", ").append(getLongitude()).append("): ");
        for (GeoLocation location : locations) {
            sb.append(location.hashCode()).append(", ");
        }
        sb.delete(sb.length() - 2, sb.length());
        return sb.toString();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        long temp;
        temp = Double.doubleToLongBits(latitude);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(longitude);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        GeoLocation other = (GeoLocation) obj;
        if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude)) {
            return false;
        }
        return Double.doubleToLongBits(longitude) == Double.doubleToLongBits(other.longitude);
    }
}
